<?php

/*

    MMetaveerse - Open Metaverse Access Library

    Copyright (c) 2009 Mary Jane Soft - Marlon J. Manrique

    http://www.maryjanesoft.com
    http://www.marlonj.com

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General
    Public License along with this library; if not, write to the
    Free Software Foundation, Inc., 59 Temple Place, Suite 330,
    Boston, MA  02111-1307  USA

    $Id$

*/

/**
 * @package mjs.mmetaverse.manager
 */

require_once 'MDataBase.class.php';
require_once 'MUser.class.php';

/**
 * User Data Base
 */
class MUserDB extends MDataBase {

    /**
     * Create the database access for the OpenSim object specified
     *
     * @param MMetaverse $metaverse current metaverse
     */
    public function MUserDB(MMetaverse $metaverse) {
        $this->MDataBase($metaverse);
        $this->db = $this->getDatabase('user');
    }

    /**
     * Return the information of the user with the specified uuid
     *
     * @param MUUID $uuid The user uuid
     * @return MUSer The information of the user
     */
    public function getUser(MUUID $uuid) {
        // Prepare the sql query
        $sql = 'select * from users where uuid=?';
        $stmt = $this->prepare($this->db,$sql);

        // Set values
        $stmt->bindValue(1,$uuid->getUnHyphenated());
        $this->execute($stmt);

        // Try to get the result
        try {
            $result = $this->fetch($stmt);
        }
        catch (Exception $e) {
            return null;
        }

        // Return user information
        return $this->fetchUser($result);
    }

    /**
     * Return the user information of the user with the names specified
     *
     * @param string $firstName First Name
     * @param string $lastName Last Name
     *
     * @return MUser The information of the user
     */
    public function getUserInfo($firstName, $lastName) {
        // Prepare the sql query
        $sql = 'select * from users where username=? and surname=?';
        $stmt = $this->prepare($this->db,$sql);

        // Set values
        $stmt->bindValue(1,$firstName);
        $stmt->bindValue(2,$lastName);
        $this->execute($stmt);

        // Try to get the result
        try {
            // Execute the sql and fetch a user
            $result = $this->fetch($stmt);
        }
        catch (Exception $e) {
            return null;
        }

        // Return user information
        return $this->fetchUser($result);
    }

    /**
     * Return the user info from the fetch array
     *
     * @param array $result SQL query result
     * @return MUser User with the data from the database
     */
    private function fetchUser(array $result) {
        // Get data
        $uuid = new MUUID($result['UUID']);
        $firstName = $result['username'];
        $lastName = $result['surname'];
        $passwordHash = $result['passwordHash'];
        $passwordSalt = $result['passwordSalt'];
        $homeRegionX = $result['homeRegionX'];
        $homeRegionY = $result['homeRegionY'];
        $homeLocationX = $result['homeLocationX'];
        $homeLocationY = $result['homeLocationY'];
        $homeLocationZ = $result['homeLocationZ'];
        $homeLookAtX = $result['homeLookAtX'];
        $homeLookAtY = $result['homeLookAtY'];
        $homeLookAtZ = $result['homeLookAtZ'];
        $created = $result['created'];
        $lastLogin = $result['lastLogin'];
        $rootInventoryFolderID = new MUUID($result['rootInventoryFolderID']);
        $userInventoryURI = $result['userInventoryURI'];
        $userAssetURI = $result['userAssetURI'];
        $profileCanDoMask = $result['profileCanDoMask'];
        $profileWantDoMask = $result['profileWantDoMask'];
        $profileAboutText = $result['profileAboutText'];
        $profileFirstText = $result['profileFirstText'];
        $profileImage = new MUUID($result['profileImage']);
        $profileFirstImage = new MUUID($result['profileFirstImage']);
        $webLoginKey = new MUUID($result['webLoginKey']);
        $homeRegionID = new MUUID($result['homeRegionID']);
        $userFlags = $result['userFlags'];
        $godLevel = $result['godLevel'];
        $customType = $result['customType'];
        $partner = new MUUID($result['partner']);
        $email = $result['email'];

        // Create the user
        $user = new MUser($uuid,$firstName,$lastName,$email,$passwordHash,
            $passwordSalt,$homeRegionX,$homeRegionY,$homeLocationX,
            $homeLocationY,$homeLocationZ,$homeLookAtX,$homeLookAtY,
            $homeLookAtZ,$created,$lastLogin,$userInventoryURI,$userAssetURI,
            $profileCanDoMask,$profileWantDoMask,$profileAboutText,
            $profileFirstText,$userFlags,$godLevel,$customType);

        // Set UUIDs
        $user->setHomeRegionID($homeRegionID);
        $user->setPartner($partner);
        $user->setProfileImage($profileImage);
        $user->setProfileFirstImage($profileFirstImage);
        $user->setRootInventoryFolderID($rootInventoryFolderID);
        $user->setWebLoginKey($webLoginKey);

        // Return the user info
        return $user;
    }

    /**
     * Check user names and password to verify the identity
     *
     * @param string $firstName First name of the user
     * @param string $lastName Last name of the user
     * @param string $password Password of the user
     * @return boolean true if the user is valid, false otherwise
     */
    public function authenticate($firstName, $lastName, $password) {
        // Prepare the sql query
        $sql = 'select username from users where username=? and surname=? and passwordHash=?';
        $stmt = $this->prepare($this->db,$sql);

        // Set values
        $stmt->bindValue(1,$firstName);
        $stmt->bindValue(2,$lastName);
        $stmt->bindValue(3,md5(md5($password).':'));

        // Execute the query
        $this->execute($stmt);

        // Fetch the result, can be empty
        $result = $stmt->fetch();

        // If result is available the user is authenticate
        if($result)
        {
            return true;
        }

        // If no result, false
        return false;
    }

    /**
     * Return the number of Users of the grid
     *
     * @return integer Number of user in the grid
     */
    public function getNumberOfUsers() {
        // Prepare the sql query
        $sql = 'select count(*) from users';
        $stmt = $this->prepare($this->db,$sql);

        // Execute the sql and fetch a user
        $this->execute($stmt);
        $result = $this->fetch($stmt);
        return (integer) $result[0];
    }

    /**
     * Return the grid users
     *
     * @return MUser[] Array with all the user of the grid
     */
    public function getUsers() {
        // Create an empty array to storage the users
        $users = array();

        // Prepare the sql query
        $sql = 'select * from users';
        $stmt = $this->prepare($this->db,$sql);

        // Execute the sql and fetch a user
        $this->execute($stmt);

        // Load users until exception is throw
        while(true)
        {
            try {
                // Load users and add to the array
                $result = $this->fetch($stmt);
                $users[] = $this->fetchUser($result);
            } catch (Exception $e) {
                // No more users
                break;
            }
        }

        // Return array of users
        return $users;
    }

    /**
     * Create the user specified
     *
     * @param MUser $user User to create
     * @todo Check which information must be provide to create a valid user
     */
    public function createUser(MUser $user) {
        // Check if the user exists
        if($this->userExists($user)) {
            throw new Exception('User exists');
        }

        // Prepare the sql
        $sql = 'insert into users(UUID,username,surname,passwordHash,' .
            'passwordSalt,homeRegionX,homeRegionY,homeLocationX,' .
            'homeLocationY,homeLocationZ,homeLookAtX,homeLookAtY,homeLookAtZ,' .
            'created,lastlogin,rootInventoryFolderID,userInventoryURI,' .
            'userAssetURI,profileCanDoMask,profileWantDoMask,profileAboutText,' .
            'profileFirstText,profileImage,profileFirstImage,webLoginKey,' .
            'homeRegionID,userFlags,godLevel,customType,partner,email' .
            ')' .
            'values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)';

        $stmt = $this->prepare($this->db,$sql);

        // Set values
        $counter = 1;
        $stmt->bindValue($counter++,$user->getUUID()->getUnHyphenated());
        $stmt->bindValue($counter++,$user->getFirstName());
        $stmt->bindValue($counter++,$user->getLastName());
        $stmt->bindValue($counter++,$user->getPasswordHash());
        $stmt->bindValue($counter++,$user->getPasswordSalt());
        $stmt->bindValue($counter++,$user->getHomeRegionX());
        $stmt->bindValue($counter++,$user->getHomeRegionY());
        $stmt->bindValue($counter++,$user->getHomeLocationX());
        $stmt->bindValue($counter++,$user->getHomeLocationY());
        $stmt->bindValue($counter++,$user->getHomeLocationZ());
        $stmt->bindValue($counter++,$user->getHomeLookAtX());
        $stmt->bindValue($counter++,$user->getHomeLookAtY());
        $stmt->bindValue($counter++,$user->getHomeLookAtZ());
        $stmt->bindValue($counter++,$user->getCreated());
        $stmt->bindValue($counter++,$user->getLastLogin());
        $stmt->bindValue($counter++,$user->getRootInventoryFolderID()->getHyphenated());
        $stmt->bindValue($counter++,$user->getUserInventoryURI());
        $stmt->bindValue($counter++,$user->getUserAssetURI());
        $stmt->bindValue($counter++,$user->getProfileCanDoMask());
        $stmt->bindValue($counter++,$user->getProfileWantDoMask());
        $stmt->bindValue($counter++,$user->getProfileAboutText());
        $stmt->bindValue($counter++,$user->getProfileFirstText());
        $stmt->bindValue($counter++,$user->getProfileImage()->getHyphenated());
        $stmt->bindValue($counter++,$user->getProfileFirstImage()->getHyphenated());
        $stmt->bindValue($counter++,$user->getWebLoginKey()->getHyphenated());
        $stmt->bindValue($counter++,$user->getHomeRegionID()->getHyphenated());
        $stmt->bindValue($counter++,$user->getUserFlags());
        $stmt->bindValue($counter++,$user->getGodLevel());
        $stmt->bindValue($counter++,$user->getCustomType());
        $stmt->bindValue($counter++,$user->getPartner()->getHyphenated());
        $stmt->bindValue($counter++,$user->getEmail());
        
        // Execute the query
        $this->execute($stmt);
    }

    /**
     * Delete the user with the uuid specified
     *
     * @param MUUID user uuid
     * @todo Check what other information must be delete 
     */
    public function deleteUser(MUUID $uuid) {
        // Prepare the sql
        $sql = 'delete from users where uuid=?';
        $stmt = $this->prepare($this->db,$sql);

        // Set values
        $stmt->bindValue(1,$uuid->getUnHyphenated());

        // Execute the sql and fetch a user
        $this->execute($stmt);
    }

    /**
     * Return the number of agents online
     *
     * @return integer Number of agents online
     */
    public function getNumberOfAgentsOnline() {
        // Prepare the sql
        $sql = 'select count(*) from useragents where agentOnLine=1';
        $stmt = $this->prepare($this->db,$sql);

        // Execute the sql and fetch a user
        $this->execute($stmt);
        $result = $this->fetch($stmt);

        // Return number
        return (integer) $result[0];
    }

    /**
     * Return the number of logins in the last month
     *
     * @return integer Number of logins in the last month
     */
    public function getNumberOfLastMonthLogins() {
        // Prepare the sql
        $time = mktime() - 2419200;
        $sql = 'select count(*) from useragents where logintime > ?';

        // Prepare the statement 
        $stmt = $this->prepare($this->db,$sql);
        $stmt->bindValue(1,$time);

        // Execute the sql and fetch a user
        $this->execute($stmt);
        $result = $this->fetch($stmt);

        // Return number
        return (integer) $result[0];
    }

    /**
     * Check if the user exists
     *
     * @param MUser $user
     * @return boolean true if the user exists, false otherwise
     */
    public function userExists(MUser $user) {
        // Get a user with the uuid
        $userInfo = $this->getUser($user->getUUID());

        // The user exists 
        if($userInfo != null)
            return true;

        // Get a user with the names
        $userInfo = $this->getUserInfo($user->getFirstName(),$user->getLastName());

        // The user exists
        if($userInfo != null)
            return true;

        // Don't exists
        return false;
    }
}
?>